package com.ticket.sass.common.ticketserver.platform.cx300

import com.google.gson.reflect.TypeToken
import com.ticket.sass.common.ticketserver.*
import com.ticket.sass.common.ticketserver.configure.ConfigData
import com.ticket.sass.common.ticketserver.dto.input.*
import com.ticket.sass.common.ticketserver.dto.output.*
import com.ticket.sass.common.ticketserver.enums.OrderTicketStatus
import com.ticket.sass.common.ticketserver.enums.SeatStatus
import com.ticket.sass.common.ticketserver.exceptions.ApiStatusException
import com.ticket.sass.common.ticketserver.exceptions.ParseDataException
import com.ticket.sass.common.ticketserver.platform.cx300.response.*
import org.apache.axis.encoding.XMLType
import org.springframework.stereotype.Component


/**
 * @author 朱其鹏
 * @date 2024/5/17 11:07
 */
@Component("ticketserver.cx300.ApiInterfaceImpl")
class ApiInterfaceImpl : ApiInterface() {
    /**
     * 配置数据
     */
    val configData
        get() = config.data as ConfigData.CX300

    override fun schedules(input: ScheduleInput): ScheduleOutput {
        val list = mutableListOf<ScheduleOutput.Schedule>()
        repeat(15) {
            val date = input.startDate.toLocalDate("yyyy-MM-dd").plusDays(it.toLong())

            try {
                val res =
                    RequestClient.send2("QueryPlanInfo", listOf(mapOf("pPlanDate" to date.toString())), configData)
                        .parseJson(object : TypeToken<List<ScheduleModel>>() {})

                val filmGroupByCode = queryFilmInfoByDatePeriod(input.startDate, input.endDate).groupBy { it.filmCode }

                list.addAll(res.filter {schedule -> schedule.setClose == 1 }.map { schedule ->
                    val film = filmGroupByCode[schedule.filmCode]!!.first()

                    val startTime = "${schedule.date} ${schedule.time}:00"
                    val endTime = startTime.toLocalDateTime("yyyy-MM-dd HH:mm:ss")
                        .plusMinutes(film.duration.toLong())
                        .format("yyyy-MM-dd HH:mm:ss")

                    ScheduleOutput.Schedule(
                        sessionCode = schedule.featureAppNo,
                        scheduleKey = "",
                        startTime = startTime,
                        endTime = endTime,
                        status = "",
                        filmName = schedule.filmName,
                        filmCode = schedule.filmCode,
                        filmVersion = film.version,
                        hallName = schedule.screenName,
                        hallCode = schedule.screenCode,
                        salePrice = 0f,
                        standPrice = schedule.standardPrice,
                        lowestPrice = schedule.lowestPrice,
                        serviceFee = 0f,
                        language = schedule.lang
                    )
                })
            } catch (e: ParseDataException) {
                e.printStackTrace()
            }
        }

        return ScheduleOutput(
            list = list,
        )
    }

    override fun scheduleSeatMap(input: ScheduleSeatMapInput): ScheduleSeatMapOutput {
        val seatMap: MutableMap<String, QueryPlanSeat> = mutableMapOf()
        try {
            val data = RequestClient.send2("QueryPlanSeat", listOf(mapOf("pFeatureAppNo" to input.sessionCode)), configData)
                .parseJson(object : TypeToken<List<QueryPlanSeat>>() {})

            data.forEach {
                seatMap[it.seatCode] = it
            }
        } catch (e: ParseDataException) {
            e.printStackTrace()
        }

        val screenSeatData = querySeatInfo(input.hallCode)

        val screenSeatMap: MutableMap<String, QuerySeatInfo> = mutableMapOf()
        val seatGroupMap: MutableMap<String, MutableList<String>> = mutableMapOf()
        screenSeatData.forEach {
            screenSeatMap["${it.yCoord}-${it.xCoord}"] = it

            if (seatGroupMap[it.groupCode] == null) {
                seatGroupMap[it.groupCode] = mutableListOf(it.xCoord)
            } else {
                seatGroupMap[it.groupCode]?.add(it.xCoord)
            }
        }

        val maxRow = screenSeatData.maxOfOrNull { it.yCoord.toInt() } ?: 0
        val maxCol = screenSeatData.maxOfOrNull { it.xCoord.toInt() } ?: 0

        val seatData: MutableList<MutableList<ScheduleSeatMapOutput.Seat>> = mutableListOf()
        for (row in 0 .. maxRow) {
            val rows = mutableListOf<ScheduleSeatMapOutput.Seat>()
            seatData.add(rows)
            for (col in 0 .. maxCol) {
                val seat = screenSeatMap["$row-$col"]
                val groupSeats = seatGroupMap[seat?.groupCode]
                val groupSeatMax = groupSeats?.maxOrNull()
                val groupSeatMin = groupSeats?.minOrNull()

                val seatInfo = ScheduleSeatMapOutput.Seat(
                    seat?.seatCode ?: "",
                    seat?.seatCode ?: "",
                    seat?.rowNum ?: "",
                    seat?.columnNum ?: "",
                    row,
                    col,
                    seatStatusFormat(seat, seatMap[seat?.seatCode]),
                    when {
                        groupSeats != null && groupSeats.size == 2 && seat?.xCoord == groupSeatMin -> -1
                        groupSeats != null && groupSeats.size == 1 -> 0
                        groupSeats != null && groupSeats.size == 2 && seat?.xCoord == groupSeatMax -> 1
                        else -> 0
                    }
                )
                seatData[row].add(seatInfo)
            }
        }

        return ScheduleSeatMapOutput(seatData)
    }

    override fun halls(): HallOutput {
        val res = RequestClient.send2(
            "QueryCinemaInfo",
            listOf(),
            configData
        ).parseJson(object : TypeToken<List<QueryCinemaInfo>>() {})

        return HallOutput(
            list = res.map {
                HallOutput.Hall(
                    name = it.screenName,
                    code = it.screenCode,
                    type = it.type,
                    seatCount = it.seatCount,
                )
            }
        )
    }

    override fun seatLock(input: SeatLockInput): SeatLockOutput {
        val res = RequestClient.send2("LockSeat", listOf(
            mapOf("FeatureAppNo" to input.sessionCode),
            mapOf("SeatCodes" to input.seatList.joinToString(",") { it.code })
        ), configData).parseJson(SeatLock::class.java)

        if (res.status == "failed") {
            throw ApiStatusException("1", "锁座失败")
        }

        return SeatLockOutput(
            id = res.orderCode,
            outLockNo = input.outLockNo,
            lockDuration = res.lockLength,
        )
    }

    override fun seatUnlock(input: SeatUnlockInput): SeatUnlockOutput {
        val res = RequestClient.send2("ReleaseSeat", listOf(
            mapOf("OrderCode" to input.id),
            mapOf("FeatureAppNo" to input.scheduleCode),
            mapOf("SeatCodes" to input.seatList.joinToString(",") { it.code })
        ), configData).parseJson(SeatUnlock::class.java)

        return SeatUnlockOutput(res.status == "success")
    }

    override fun orderTicketSubmit(input: OrderTicketSubmitInput): OrderTicketSubmitOutput {

        val res = RequestClient.send2(
            "SubmitOrder",
            listOf(
                mapOf("OrderCode" to input.orderNo),
                mapOf("FeatureAppNo" to input.sessionCode),
                mapOf("MobilePhone" to input.cellphone),
                mapOf("SeatCodes" to input.seatList.joinToString(",") { it.code }),
                mapOf("TotalPrice" to input.seatList.sumOf {
                    it.price + it.serviceFee + it.handleFee + it.channelServiceFee
                }),
                mapOf("ServiceCharge" to input.seatList.sumOf { it.channelServiceFee })
            ),
            configData,
            listOf("TotalPrice", "ServiceCharge")
        ).parseJson(SubmitOrder::class.java)

        if (res.status == "failed") throw ApiStatusException(res.errorCode, "提交订单失败")

        return OrderTicketSubmitOutput(
            orderNo = res.orderCode,
            takeCodeList = listOf(
                OrderTicketSubmitOutput.TakeCode("序列号", res.printNo),
            ),
            seatList = res.seatInfos.map {
                OrderTicketSubmitOutput.Seat(
                    id = it.seatCode,
                    code = it.seatCode,
                    filmTicketCode = it.filmTicketCode,
                    ticketInfoCode = it.ticketInfoCode,
                )
            }
        )
    }

    override fun orderTicketStatus(input: OrderTicketStatusInput): OrderTicketStatusOutput {
        val res = RequestClient.send2(
            "QueryOrderStatus",
            listOf(
                mapOf("pOrderCode" to input.orderNo)
            ),
            configData
        ).parseJson(QueryOrderStatus::class.java)

        val status = when (res.orderStatus) {
            "0" -> OrderTicketStatus.SUCCESS
            "1" -> OrderTicketStatus.FAIL
            "2" -> OrderTicketStatus.CANCEL
            else -> OrderTicketStatus.CANCEL
        }

        return OrderTicketStatusOutput(
            status,
            takeCodes = listOf(),
            seatList = listOf()
        )
    }

    override fun orderTicketInfo(input: OrderTicketInfoInput): OrderTicketInfoOutput {
        try {
            val res = RequestClient.send2(
                "QueryTicketInfo",
                listOf(
                    mapOf("PrintNo" to input.code),
                ),
                configData
            ).parseJson(object : TypeToken<List<QueryTicketInfo>>() {})

            val firstTicket = res.first()

            return OrderTicketInfoOutput(
                cinemaName = "",
                startTime = firstTicket.startTime.toLocalDateTime("yyyy-MM-dd'T'HH:mm:ss").format("yyyy-MM-dd HH:mm:ss"),
                screenCode = firstTicket.screenCode,
                screenName = firstTicket.screenName,
                sessionCode = firstTicket.featureAppNo,
                filmCode = firstTicket.filmCode,
                filmName = firstTicket.filmName,
                tickets = res.map {
                    val seatSplit = it.seatCode.split("#")
                    OrderTicketInfoOutput.Ticket(
                        qrCode = it.ticketInfoCode,
                        ticketCode = it.ticketCode,
                        printFlag = it.status.toString(),
                        row = seatSplit[1],
                        col = seatSplit[2],
                        seatCode = it.seatCode,
                        seat = it.seatName,
                        ticketPrice = it.price,
                        service = it.service
                    )
                },
                printId = input.code,
                ""
            )
        } catch (e: ParseDataException) {
            throw ApiStatusException("404", "订单不存在")
        }
    }

    override fun orderTicketRefund(input: OrderTicketRefundInput): OrderTicketRefundOutput {
        val res = RequestClient.send2("ReleaseSeat", listOf(
            mapOf("OrderCode" to input.orderNo),
            mapOf("FeatureAppNo" to input.sessionCode),
            mapOf("SeatCodes" to input.seatList.joinToString(",") { it.code })
        ), configData).parseJson(SeatUnlock::class.java)

        return OrderTicketRefundOutput(res.status == "success")
    }

    /**
     * 获取影片信息
     *
     * @param startDate
     * @param endDate
     *
     * @return [QueryFilmList]
     */
    private fun queryFilmInfoByDatePeriod(startDate: String, endDate: String): List<QueryFilmList> {
        return RequestClient.send2(
            "QueryFilmInfoByDatePeriod",
            listOf(
                mapOf("pPlanStartDate" to startDate),
                mapOf("pPlanEndDate" to endDate),
            ),
            configData
        ).parseJson(object : TypeToken<List<QueryFilmList>>() {})
    }

    /**
     * 查询影厅座位信息
     *
     * @param screenCode 影厅编码
     *
     * @return [QuerySeatInfo]
     */
    private fun querySeatInfo(screenCode: String): List<QuerySeatInfo> {
        return RequestClient.send2(
            "QuerySeatInfo",
            listOf(
                mapOf("pScreenCode" to screenCode),
            ),
            configData
        ).parseJson(object : TypeToken<List<QuerySeatInfo>>() {})
    }

    /**
     * 检查接口响应数据状态
     */
    private fun BaseResult.checkApiStatus() {
        if (resultCode != 0) {
            throw ApiStatusException(resultCode.toString(), message)
        }
    }

    /**
     * 格式化座位状态
     *
     * @param seat 座位信息
     *
     * @return 状态
     */
    private fun seatStatusFormat(seatInfo: QuerySeatInfo?, seat: QueryPlanSeat?): Short {
        if (seatInfo == null) return SeatStatus.BLANK.value

        return if (seat != null) SeatStatus.SOLD.value else SeatStatus.AVAILABLE.value
    }
}